package com.njit.mailm.core.service.impl;

import com.njit.mailm.commmon.utils.GUID;
import com.njit.mailm.commmon.utils.HTMLSpirit;
import com.njit.mailm.core.bean.*;
import com.njit.mailm.core.dao.MailDao;
import com.njit.mailm.core.service.MailReciverBaseService;
import com.njit.mailm.core.service.MailService;
import com.sun.mail.pop3.POP3Folder;
import com.sun.mail.pop3.POP3Store;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

@Transactional
public class MailServiceImpl extends MailReciverBaseService  implements MailService{

	@Autowired
	private MailDao mailDao;
	
	public List<MessageWrap> synchrMail(Email email) {
		Session session = createSession("pop3",email.getPop().getPort(),email.getPop().getIp());
		POP3Store store = null;
		POP3Folder inbox = null;
		List<MessageWrap> msgs = new ArrayList<MessageWrap>();
		try {
			store = (POP3Store) session.getStore("pop3");
			store.connect(email.getAccount(), email.getPassword());

			inbox = (POP3Folder) store.getFolder("INBOX");
			inbox.open(Folder.READ_WRITE);
			FetchProfile profile = new FetchProfile();
			profile.add(UIDFolder.FetchProfileItem.UID);
			Message[] messages = inbox.getMessages();
			inbox.fetch(messages, profile);

			List<MessageWrap> messageWraps = new ArrayList<MessageWrap>();

			for(int i = messages.length - 1;i>0;i--){
				String guid = inbox.getUID(messages[i]);
				MessageWrap mw = mailDao.selectMessageById(guid);
				if(mw != null){
					continue;
				}
				MessageWrap mWrap = packMessage(inbox,messages[i],email);
				msgs.add(mWrap);
				messageWraps.add(mWrap);

			}
			if(messageWraps.size() > 0 ){
				mailDao.batchInsertMessage(messageWraps);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			close(inbox,store);
		}
		return msgs;
	}

	public MessageWrap packMessage(POP3Folder inbox,Message message,Email email){
		MessageWrap mWrap = new MessageWrap();
		try {
			mWrap.setId(inbox.getUID(message));
			mWrap.setSubject(message.getSubject());
			mWrap.setFromName(getFrom(message));
			mWrap.setTo(getReceiveAddress(message, Message.RecipientType.TO));
			mWrap.setCopyTo(getReceiveAddress(message, Message.RecipientType.CC));
			mWrap.setBcc(getReceiveAddress(message, Message.RecipientType.BCC));
			StringBuffer content = new StringBuffer();
			getMailTextContent(message, content);
			mWrap.setBody(content.toString());
			System.out.println();
			String contentprev = HTMLSpirit.delHTMLTag(content.toString());
			System.out.println("邮件内容" + contentprev);
			if (contentprev.length() > 10) {
				contentprev = contentprev.substring(0, 10) + "...";
			}
			mWrap.setContentPrev(contentprev);
			mWrap.setSendtime(getSentDate(message, "yyyy-MM-dd HH:mm:ss"));
			mWrap.setSize(message.getSize() * 1024 + "kb");

			Address[] froms = message.getFrom();
			if (froms.length < 1)
				throw new MessagingException("没有发件人!");

			InternetAddress address = (InternetAddress) froms[0];
			String from = address.getAddress();
			mWrap.setFrom(from);
			mWrap.setEmail(email);
			return mWrap;
		} catch (MessagingException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	@Transactional(readOnly = true)
	public List<MessageWrap> listAll() {
		return mailDao.listAll();
	}

	protected boolean bindPOP(Email email){
		Session session = createSession("pop3",email.getPop().getPort(),email.getPop().getIp());
		POP3Store store = null;
		POP3Folder inbox = null;
		try {
			store = (POP3Store) session.getStore("pop3");
			store.connect(email.getAccount(), email.getPassword());

			inbox = (POP3Folder) store.getFolder("INBOX");
			inbox.open(Folder.READ_WRITE);
			FetchProfile profile = new FetchProfile();
			profile.add(UIDFolder.FetchProfileItem.UID);
			Message[] messages = inbox.getMessages();
			inbox.fetch(messages, profile);
			return store.isConnected();
		} catch (Exception e) {
			return false;
		}finally {
			close(inbox, store);
		}
		
	}
	
	protected boolean bindSmtp(Email email){
		Session session = createSession("smtp", email.getSmtp().getPort(), email.getSmtp().getIp());
		Transport transport = null;
		boolean connect = false;
		try {
			transport = session.getTransport();
			transport.connect(email.getSmtp().getIp(),email.getAccount(), email.getPassword());
			connect =  transport.isConnected();
		} catch (Exception e) {
			return false;
		}finally {
			try {
				transport.close();
			} catch (MessagingException e) {
				e.printStackTrace();
			}         
		}
		return connect;
	}
	
	public boolean bindMail(Email email) {
		return bindSmtp(email) && bindPOP(email);
	}

//	public void batchInsertMessage(List<MessageWrap> messages) {
//		mailDao.batchInsertMessage(messages);
//	}

	@Transactional(readOnly = true)
	public MessageWrap selectMessageById(String id) {
		return mailDao.selectMessageById(id);
	}

	@Transactional(readOnly = true)
	public List<MessageWrap> pageList(Page page,Email email) {
		return mailDao.pageList(page,email);
	}

	@Transactional(readOnly = true)
	public int getMailTotal(Email email) {

		return mailDao.getMailTotal(email);

	}

	/**
	 * 将用户绑定的邮箱存储
	 */
	public boolean insertMail(Email email) {

		String popId;
		popId = mailDao.findPop(email.getPop().getIp());
		String smtpId;
		smtpId = mailDao.findSmtp(email.getSmtp().getIp());
		if(popId == null ){
			popId = GUID.getGUID();
			Pop pop = email.getPop();
			pop.setId(popId);
			mailDao.insertPop(pop);
		}
		if(smtpId == null){
			smtpId = GUID.getGUID();
			Smtp smtp = email.getSmtp();
			smtp.setId(smtpId);
			mailDao.insertSmtp(smtp);
		}
		Smtp smtp = email.getSmtp();
		smtp.setId(smtpId);
		Pop pop = email.getPop();
		pop.setId(popId);
		email.setPop(pop);
		email.setSmtp(smtp);
		email.setId(GUID.getGUID());
		try {
			mailDao.insertMail(email);

		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("插入邮箱错误");
			return false;
		}
		return  true;
	}

	public List<Email> getEmailsByUser(User user) {

		return mailDao.getEmailsByUser(user);
	}

	public Email getEmailById(String id) {
		return mailDao.getEmailById(id);
	}

	public List<Message> getSynchronMessages(Email email) {
		Session session = createSession("pop3",email.getPop().getPort(),email.getPop().getIp());
		POP3Store store = null;
		POP3Folder inbox = null;
		List<Message> resultList = new ArrayList<Message>();
		try {
			store = (POP3Store) session.getStore("pop3");
			store.connect(email.getAccount(), email.getPassword());

			inbox = (POP3Folder) store.getFolder("INBOX");
			inbox.open(Folder.READ_WRITE);
			FetchProfile profile = new FetchProfile();
			profile.add(UIDFolder.FetchProfileItem.UID);
			Message[] messages = inbox.getMessages();
			inbox.fetch(messages, profile);



			for(int i = messages.length - 1;i>0;i--){
				String guid = inbox.getUID(messages[i]);
				MessageWrap mw = mailDao.selectMessageById(guid);
				if(mw != null){
					continue;
				}
				resultList.add(messages[i]);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(!(resultList.size() > 0) ){
				close(inbox,store);
			}
		}
		return resultList;
	}

	public Map<String,Object> batchSynchron(List<Message> batchList, Email email) {
		Session session = createSession("pop3",email.getPop().getPort(),email.getPop().getIp());
		POP3Store store = null;
		POP3Folder inbox = null;
		Map<String,Object> result = new HashMap<String, Object>();
		try {
			store = (POP3Store) session.getStore("pop3");
			if(!store.isConnected()){
				store.connect(email.getAccount(), email.getPassword());
			}


			inbox = (POP3Folder) store.getFolder("INBOX");
			inbox.open(Folder.READ_WRITE);
			FetchProfile profile = new FetchProfile();
			profile.add(UIDFolder.FetchProfileItem.UID);
			Message[] messages = inbox.getMessages();
			inbox.fetch(messages, profile);



			for(Message msg :batchList){
				MessageWrap mWrap = packMessage(inbox, msg, email);
				insertMessage(mWrap);
			}




		} catch (Exception e) {
			e.printStackTrace();
		} finally {
		}

		result.put("store",store);
		result.put("folder", inbox);
		return result;
	}

	protected void insertMessage(MessageWrap mWrap) {
		try {
			mailDao.insertMessage(mWrap);
			if(mWrap.getTos().length>0){
                mailDao.insertTO(mWrap);
            }
			if(mWrap.getBccs().length > 0){
                mailDao.insertBcc(mWrap);
            }
			if(mWrap.getCcs().length > 0){
                mailDao.insertCc(mWrap);
            }
		} catch (Exception e) {
			//
		}
	}

	public void colseFolder(Store store, POP3Folder folder) {
		close(folder,store);
	}

	protected Store getConnectedStore(Email email){
		Session session = createSession("pop3",email.getPop().getPort(),email.getPop().getIp());
		POP3Store store = null;
		try {
			store = (POP3Store) session.getStore("pop3");
			if(!store.isConnected()){
				store.connect(email.getAccount(), email.getPassword());
			}

		}catch (Exception e){
			e.printStackTrace();
		}
		return store;
	}

	protected POP3Folder getInbox(Store store,Email email){
		POP3Folder inbox = null;

		if(!store.isConnected() || store == null){
			store = getConnectedStore(email);
		}
		try {
			inbox = (POP3Folder) store.getFolder("INBOX");
			inbox.open(Folder.READ_WRITE);

		} catch (MessagingException e) {
			e.printStackTrace();
		}
		return inbox;
	}

	protected Message[] getMessages(POP3Folder inbox){
		try {
			FetchProfile profile = new FetchProfile();
			profile.add(UIDFolder.FetchProfileItem.UID);
			Message[] messages = inbox.getMessages();
			inbox.fetch(messages, profile);
			return messages;
		} catch (MessagingException e) {
			e.printStackTrace();
			return null;
		}

	}


	public Map<String, Object> intervalSynchron(List<Email> emails) {
		Map<String,Object> result = new HashMap<String, Object>();
		result.put("newNumber",0);
		for(Email email:emails){
			Store store = getConnectedStore(email);
			POP3Folder inbox = getInbox(store, email);
			Message[] messages = getMessages(inbox);
			List<MessageWrap> msg = new ArrayList<MessageWrap>();

			try {
				for(int i = messages.length - 1;i>0;i--){
                    String guid = inbox.getUID(messages[i]);
                    MessageWrap mw = mailDao.selectMessageById(guid);
                    if(mw != null){
                        continue;
                    }
                    MessageWrap messageWrap = packMessage(inbox, messages[i], email);
					insertMessage(messageWrap);
                    msg.add(messageWrap);
                }
				if(msg.size()> 0){
					result.put("newNumber",msg.size());
					result.put("from",msg.get(0).getFrom());
					result.put("subject",msg.get(0).getSubject());
					result.put("id",email.getId());
				}
			} catch (MessagingException e) {
				e.printStackTrace();
			}finally {
				close(inbox,store);
			}
		}

		return result;
	}


	/**
	 * 获得邮件主题
	 * @param msg 邮件内容
	 * @return 解码后的邮件主题
	 */
	protected static String getSubject(Message msg) throws UnsupportedEncodingException, MessagingException {
		return MimeUtility.decodeText(msg.getSubject());
	}

	/**
	 * 获得邮件发件人
	 * @param msg 邮件内容
	 * @return 姓名 <Email地址>
	 * @throws MessagingException
	 * @throws UnsupportedEncodingException
	 */
	protected static String getFrom(Message msg) throws MessagingException, UnsupportedEncodingException {
		String from = "";
		Address[] froms = msg.getFrom();
		if (froms == null || froms.length < 1  )
			throw new MessagingException("没有发件人!");

		InternetAddress address = (InternetAddress) froms[0];
		String person = address.getPersonal();

		if (person != null) {
			person = MimeUtility.decodeText(person) + " ";
		} else {
			person = address.getAddress();
		}
		from = person + "<" + address.getAddress() + ">";

		return from;
	}

	/**
	 * 根据收件人类型，获取邮件收件人、抄送和密送地址。如果收件人类型为空，则获得所有的收件人
	 * <p>Message.RecipientType.TO  收件人</p>
	 * <p>Message.RecipientType.CC  抄送</p>
	 * <p>Message.RecipientType.BCC 密送</p>
	 * @param msg 邮件内容
	 * @param type 收件人类型
	 * @return 收件人1 <邮件地址1>, 收件人2 <邮件地址2>, ...
	 * @throws MessagingException
	 */
	protected static String getReceiveAddress(Message msg, Message.RecipientType type) throws MessagingException {
		StringBuffer receiveAddress = new StringBuffer();
		Address[] addresss = null;
		if (type == null) {
			addresss = msg.getAllRecipients();
		} else {
			addresss = msg.getRecipients(type);
		}

		if (addresss == null || addresss.length < 1)
			return "";
		for (Address address : addresss) {
			InternetAddress internetAddress = (InternetAddress)address;
			receiveAddress.append(internetAddress.toUnicodeString()).append(",");
		}

		receiveAddress.deleteCharAt(receiveAddress.length()-1); //删除最后一个逗号

		return receiveAddress.toString();
	}

	/**
	 * 获得邮件发送时间
	 * @param msg 邮件内容
	 * @return yyyy年mm月dd日 星期X HH:mm
	 * @throws MessagingException
	 */
	protected static String getSentDate(Message msg, String pattern) throws MessagingException {
		Date receivedDate = msg.getSentDate();
		if (receivedDate == null)
			return "";

		if (pattern == null || "".equals(pattern))
			pattern = "yyyy年MM月dd日 E HH:mm ";

		return new SimpleDateFormat(pattern).format(receivedDate);
	}

	/**
	 * 判断邮件中是否包含附件
	 * @return 邮件中存在附件返回true，不存在返回false
	 * @throws MessagingException
	 * @throws IOException
	 */
	protected boolean isContainAttachment(Part part) throws MessagingException, IOException {
		boolean flag = false;
		if (part.isMimeType("multipart/*")) {
			MimeMultipart multipart = (MimeMultipart) part.getContent();
			int partCount = multipart.getCount();
			for (int i = 0; i < partCount; i++) {
				BodyPart bodyPart = multipart.getBodyPart(i);
				String disp = bodyPart.getDisposition();
				if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
					flag = true;
				} else if (bodyPart.isMimeType("multipart/*")) {
					flag = isContainAttachment(bodyPart);
				} else {
					String contentType = bodyPart.getContentType();
					if (contentType.indexOf("application") != -1) {
						flag = true;
					}

					if (contentType.indexOf("name") != -1) {
						flag = true;
					}
				}

				if (flag) break;
			}
		} else if (part.isMimeType("message/rfc822")) {
			flag = isContainAttachment((Part)part.getContent());
		}
		return flag;
	}

	/**
	 * 判断邮件是否已读
	 * @param msg 邮件内容
	 * @return 如果邮件已读返回true,否则返回false
	 * @throws MessagingException
	 */
	protected boolean isSeen(MimeMessage msg) throws MessagingException {
		return msg.getFlags().contains(Flags.Flag.SEEN);
	}

	/**
	 * 判断邮件是否需要阅读回执
	 * @param msg 邮件内容
	 * @return 需要回执返回true,否则返回false
	 * @throws MessagingException
	 */
	protected boolean isReplySign(Message msg) throws MessagingException {
		boolean replySign = false;
		String[] headers = msg.getHeader("Disposition-Notification-To");
		if (headers != null)
			replySign = true;
		return replySign;
	}

	/**
	 * 获得邮件的优先级
	 * @param msg 邮件内容
	 * @return 1(High):紧急  3:普通(Normal)  5:低(Low)
	 * @throws MessagingException
	 */
	protected String getPriority(Message msg) throws MessagingException {
		String priority = "普通";
		String[] headers = msg.getHeader("X-Priority");
		if (headers != null) {
			String headerPriority = headers[0];
			if (headerPriority.indexOf("1") != -1 || headerPriority.indexOf("High") != -1)
				priority = "紧急";
			else if (headerPriority.indexOf("5") != -1 || headerPriority.indexOf("Low") != -1)
				priority = "低";
			else
				priority = "普通";
		}
		return priority;
	}

	/**
	 * 获得邮件文本内容
	 * @param part 邮件体
	 * @param content 存储邮件文本内容的字符串
	 * @throws MessagingException
	 * @throws IOException
	 */
	protected void getMailTextContent(Part part, StringBuffer content) throws MessagingException, IOException {
		//如果是文本类型的附件，通过getContent方法可以取到文本内容，但这不是我们需要的结果，所以在这里要做判断
		boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
		if (part.isMimeType("text/*") && !isContainTextAttach) {
			content.append(part.getContent().toString());
		} else if (part.isMimeType("message/rfc822")) {
			getMailTextContent((Part)part.getContent(),content);
		} else if (part.isMimeType("multipart/*")) {
			Multipart multipart = (Multipart) part.getContent();
			int partCount = multipart.getCount();
			for (int i = 0; i < partCount; i++) {
				BodyPart bodyPart = multipart.getBodyPart(i);
				getMailTextContent(bodyPart,content);
			}
		}
	}

	/**
	 * 保存附件
	 * @param part 邮件中多个组合体中的其中一个组合体
	 * @param destDir  附件保存目录
	 * @throws UnsupportedEncodingException
	 * @throws MessagingException
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	protected void saveAttachment(Part part, String destDir) throws UnsupportedEncodingException, MessagingException,
			FileNotFoundException, IOException {
		if (part.isMimeType("multipart/*")) {
			Multipart multipart = (Multipart) part.getContent();    //复杂体邮件
			//复杂体邮件包含多个邮件体
			int partCount = multipart.getCount();
			for (int i = 0; i < partCount; i++) {
				//获得复杂体邮件中其中一个邮件体
				BodyPart bodyPart = multipart.getBodyPart(i);
				//某一个邮件体也有可能是由多个邮件体组成的复杂体
				String disp = bodyPart.getDisposition();
				if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
					InputStream is = bodyPart.getInputStream();
					saveFile(is, destDir, decodeText(bodyPart.getFileName()));
				} else if (bodyPart.isMimeType("multipart/*")) {
					saveAttachment(bodyPart,destDir);
				} else {
					String contentType = bodyPart.getContentType();
					if (contentType.indexOf("name") != -1 || contentType.indexOf("application") != -1) {
						saveFile(bodyPart.getInputStream(), destDir, decodeText(bodyPart.getFileName()));
					}
				}
			}
		} else if (part.isMimeType("message/rfc822")) {
			saveAttachment((Part) part.getContent(),destDir);
		}
	}

	/**
	 * 读取输入流中的数据保存至指定目录
	 * @param is 输入流
	 * @param fileName 文件名
	 * @param destDir 文件存储目录
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	protected void saveFile(InputStream is, String destDir, String fileName)
			throws FileNotFoundException, IOException {
		BufferedInputStream bis = new BufferedInputStream(is);
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(new File(destDir + fileName)));
		int len = -1;
		while ((len = bis.read()) != -1) {
			bos.write(len);
			bos.flush();
		}
		bos.close();
		bis.close();
	}

	/**
	 * 文本解码
	 * @param encodeText 解码MimeUtility.encodeText(String text)方法编码后的文本
	 * @return 解码后的文本
	 * @throws UnsupportedEncodingException
	 */
	protected String decodeText(String encodeText) throws UnsupportedEncodingException {
		if (encodeText == null || "".equals(encodeText)) {
			return "";
		} else {
			return MimeUtility.decodeText(encodeText);
		}
	}
}
